package com.yjb.tenet.boot.system.application.manager;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.yjb.tenet.boot.common.constants.GlobalConstants;
import com.yjb.tenet.boot.common.enums.CommonStatusEnum;
import com.yjb.tenet.boot.common.enums.RoleCodeEnum;
import com.yjb.tenet.boot.common.exception.CustomException;
import com.yjb.tenet.boot.common.exception.enums.ErrorCodeEnum;
import com.yjb.tenet.boot.framework.satoken.core.LoginUser;
import com.yjb.tenet.boot.framework.satoken.core.utils.SecurityFrameworkUtils;
import com.yjb.tenet.boot.system.application.convert.SysMenuConvert;
import com.yjb.tenet.boot.system.application.service.SysMenuService;
import com.yjb.tenet.boot.system.application.service.SysRoleMenuService;
import com.yjb.tenet.boot.system.application.service.SysRoleService;
import com.yjb.tenet.boot.system.application.service.SysUserRoleService;
import com.yjb.tenet.boot.system.domain.entity.SysMenu;
import com.yjb.tenet.boot.system.domain.entity.SysRole;
import com.yjb.tenet.boot.system.domain.entity.SysRoleMenu;
import com.yjb.tenet.boot.system.domain.entity.SysUserRole;
import com.yjb.tenet.boot.system.domain.form.SysMenuAddForm;
import com.yjb.tenet.boot.system.domain.form.SysMenuEditForm;
import com.yjb.tenet.boot.system.infrastructure.enums.SysMenuTypeEnum;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * @author yinjinbiao
 * @version 1.0
 * @description
 * @create 2022/4/28 10:40
 */
@Component
public class SysMenuManager {

	private static final Long TREE_ROOT_ID = 0L;

	@Resource
    private SysMenuService sysMenuService;

    @Resource
    private SysUserRoleService sysUserRoleService;

    @Resource
    private SysRoleMenuService sysRoleMenuService;

    @Resource
    private SysRoleService sysRoleService;

    /**
     * 菜单树结构
     *
     * @return
     */
    public List<Tree<Long>> treeMenu() {
        // 查询所有菜单，并转换为TreeNode节点。
        List<TreeNode<Long>> nodeList = sysMenuService.lambdaQuery()
                .eq(SysMenu::getDeleted, GlobalConstants.NOT_DELETED)
                .orderByAsc(SysMenu::getOrderNo).list().stream()
                .map(getNodeFunction()).collect(Collectors.toList());

        return TreeUtil.build(nodeList, TREE_ROOT_ID);
    }

    /**
     * 根据名字搜索菜单
     */
    public List<SysMenu> getTreeNode(String name) {
        LambdaUpdateWrapper<SysMenu> wrapper = new LambdaUpdateWrapper<>(SysMenu.class);
        wrapper.eq(SysMenu::getDeleted, GlobalConstants.NOT_DELETED);
        wrapper.like(SysMenu::getName, name);
        wrapper.orderByAsc(SysMenu::getOrderNo);
        return sysMenuService.list(wrapper);
    }

    /**
     * 保存菜单
     *
     * @param sysMenuAddForm
     * @return
     */
    public boolean save(SysMenuAddForm sysMenuAddForm) {
        Long parentId = sysMenuAddForm.getParentId();
        if (!parentId.equals(TREE_ROOT_ID)) {
            String type = sysMenuService.getById(parentId).getType();
	        if (SysMenuTypeEnum.BUTTON.getValue().equals(type)&&SysMenuTypeEnum.MENU.getValue().equals(sysMenuAddForm.getType())) {
		        throw new CustomException(ErrorCodeEnum.BUSINESS_FAIL.getCode(), "按钮下不能添加菜单");
	        }
        }
        return sysMenuService.save(SysMenuConvert.INSTANCE.convert(sysMenuAddForm));
    }

    /**
     * 编辑菜单
     *
     * @param sysMenuEditForm
     * @return
     */
    public boolean edit(SysMenuEditForm sysMenuEditForm) {
        Long parentId = sysMenuEditForm.getParentId();
        if (!parentId.equals(TREE_ROOT_ID)) {
            String type = sysMenuService.getById(parentId).getType();
	        if (SysMenuTypeEnum.BUTTON.getValue().equals(type)&&SysMenuTypeEnum.MENU.getValue().equals(sysMenuEditForm.getType())) {
		        throw new CustomException(ErrorCodeEnum.BUSINESS_FAIL.getCode(), "按钮下不能添加菜单");
	        }
        }
        return sysMenuService.updateById(SysMenuConvert.INSTANCE.convert(sysMenuEditForm));
    }

    /**
     * 删除
     *
     * @param id
     * @return
     */
    public boolean delete(Long id) {
        SysMenu one = sysMenuService.lambdaQuery()
                .eq(SysMenu::getDeleted, GlobalConstants.NOT_DELETED)
                .eq(SysMenu::getId, id)
                .one();
        List<SysMenu> list = sysMenuService.lambdaQuery()
                .eq(SysMenu::getDeleted, GlobalConstants.NOT_DELETED)
                .eq(SysMenu::getParentId, id)
                .list();
        if (CollectionUtil.isNotEmpty(list)) {
            throw new CustomException(ErrorCodeEnum.PARAM_ILLEGAL.getCode(), "该节点下存在子节点,请先删除子节点");
        }
        one.setDeleted(one.getId());
        return sysMenuService.updateById(one);
    }

    @NotNull
    private Function<SysMenu, TreeNode<Long>> getNodeFunction() {
        return menu -> {
            TreeNode<Long> node = new TreeNode<>();
            node.setId(menu.getId());
            node.setName(menu.getName());
            node.setParentId(menu.getParentId());
            node.setWeight(menu.getOrderNo());
            // 扩展属性
            Map<String, Object> extra = new HashMap<>();
            extra.put("icon", menu.getIcon());
            extra.put("path", menu.getPath());
            extra.put("type", menu.getType());
            extra.put("permission", menu.getPermission());
            extra.put("orderNo", menu.getOrderNo());
            extra.put("hidden", menu.getHidden());
            extra.put("keepAlive", menu.getKeepAlive());
            extra.put("openType", menu.getOpenType());
            node.setExtra(extra);
            return node;
        };
    }

    /**
     * 获取当前用户的有效菜单树
     *
     * @return
     */
    public List<Tree<Long>> getMyMenu() {
	    LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        // 判断是否是超级账号，显示所有菜单
        if (RoleCodeEnum.hasAnySuperAdmin(loginUser.getRoleCodes())) {
            List<TreeNode<Long>> nodeList = sysMenuService.lambdaQuery()
                    .eq(SysMenu::getDeleted, GlobalConstants.NOT_DELETED)
                    .eq(SysMenu::getType, SysMenuTypeEnum.MENU.getValue())
                    .eq(SysMenu::getHidden, CommonStatusEnum.DISABLE.getValue())
                    .orderByAsc(SysMenu::getOrderNo).list().stream()
                    .map(getNodeFunction()).collect(Collectors.toList());

            return TreeUtil.build(nodeList, TREE_ROOT_ID);
        }

        // 根据用户 id 获取所有 用户的所有角色id，可能存在已被删除的角色id
        List<Long> myAllRoleIdList = sysUserRoleService.lambdaQuery().eq(SysUserRole::getUserId, loginUser.getId()).list().stream().map(sysUserRole -> {
            return sysUserRole.getRoleId();
        }).collect(Collectors.toList());
        // 如果用户没有分配过角色
        if (CollectionUtil.isEmpty(myAllRoleIdList)) {
            return new ArrayList<Tree<Long>>();
        }
        // 过滤掉已被删除的角色id
        List<Long> myValidRoleIdList = sysRoleService.lambdaQuery().eq(SysRole::getDeleted, GlobalConstants.NOT_DELETED).in(SysRole::getId, myAllRoleIdList).list().stream().map(sysRole -> {
            return sysRole.getId();
        }).collect(Collectors.toList());
        // 如果角色都被删除了
        if (CollectionUtil.isEmpty(myValidRoleIdList)) {
            return new ArrayList<Tree<Long>>();
        }
        // 所有菜单id
        List<Long> menuIdList = sysRoleMenuService.lambdaQuery().in(SysRoleMenu::getRoleId, myValidRoleIdList).list().stream().map(sysRoleMenu -> {
            return sysRoleMenu.getMenuId();
        }).collect(Collectors.toList());
        // 如果角色没有分配过菜单
        if (CollectionUtil.isEmpty(menuIdList)) {
            return new ArrayList<Tree<Long>>();
        }
        // 过滤已被删除的菜单
        List<TreeNode<Long>> nodeList = sysMenuService.lambdaQuery()
                .eq(SysMenu::getDeleted, GlobalConstants.NOT_DELETED)
                .eq(SysMenu::getType, SysMenuTypeEnum.MENU.getValue())
                .in(SysMenu::getId, menuIdList)
                .orderByAsc(SysMenu::getOrderNo)
                .list().stream().map(getNodeFunction()).collect(Collectors.toList());
        return TreeUtil.build(nodeList, TREE_ROOT_ID);

    }
}
